﻿using System.Reflection;
using System.Security.Cryptography;
using FreeSql.Internal;
using KrasDD.Extensions;
using KrasDD.Models;
using Microsoft.Extensions.Hosting;

namespace KrasDD
{
    public static class AppContainer
    {
        public static IServiceProvider ServiceProvider { get; set; }
        public static IFreeSql? Ifsq => ServiceProvider?.GetService<IFreeSql>();
        public static IDistributedCache? Cache =>
            ServiceProvider?.GetRequiredService<IDistributedCache>();
        public static IHttpContextAccessor? HttpContextAccessor =>
            ServiceProvider?.GetService<IHttpContextAccessor>();

        public static Dictionary<string, HotReloadPluginManager> PluginManager =
            new Dictionary<string, HotReloadPluginManager>();

        public static IFreeSql CreateFreeSql(WebApplicationBuilder? builder = null)
        {
            if (Ifsq != null)
            {
                return Ifsq;
            }
            var connectionString = builder?.Configuration.GetConnectionString("DefaultConnection");
            if (builder == null)
            {
                connectionString = ServiceProvider
                    .GetService<IConfiguration>()
                    ?.GetConnectionString("DefaultConnection");
            }
            IFreeSql fsql = new FreeSql.FreeSqlBuilder()
                .UseConnectionString(FreeSql.DataType.SqlServer, connectionString)
                .CreateDatabaseIfNotExistsSqlServer()
                .CreateSchemaIfNotExistsSqlServer("innovator") // 创建 innovator 架构
                .UseAdoConnectionPool(true)
                .UseMonitorCommand(cmd => Console.WriteLine($"Sql：{cmd.CommandText}"))
                .UseAutoSyncStructure(true) //自动同步实体结构到数据库，只有CRUD时才会生成表
                .Build();

            // 添加静态标志来控制权限过滤器是否启用
            bool permissionFilterEnabled = false;

            fsql.GlobalFilter.ApplyCore<AppBaseEntity>(
                false,
                "permission",
                () => permissionFilterEnabled, // 只有在启用时才应用权限过滤器
                l =>
                    l.HasACCESS2(
                        l.PERMISSION_ID,
                        l.CREATED_BY_ID,
                        l.OWNED_BY_ID,
                        l.TEAM_ID,
                        true,
                        false,
                        false
                    ),
                before: false,
                GlobalFilter.FilterType.Query
            );
            fsql.GlobalFilter.ApplyCore<AppBaseEntity>(
                false,
                "permission1",
                () => permissionFilterEnabled, // 只有在启用时才应用权限过滤器
                l =>
                    l.HasACCESS2(
                        l.PERMISSION_ID,
                        l.CREATED_BY_ID,
                        l.OWNED_BY_ID,
                        l.TEAM_ID,
                        false,
                        true,
                        false
                    ),
                before: false,
                GlobalFilter.FilterType.Update
            );
            fsql.GlobalFilter.ApplyCore<AppBaseEntity>(
                false,
                "permission2",
                () => permissionFilterEnabled, // 只有在启用时才应用权限过滤器
                l =>
                    l.HasACCESS2(
                        l.PERMISSION_ID,
                        l.CREATED_BY_ID,
                        l.OWNED_BY_ID,
                        l.TEAM_ID,
                        false,
                        false,
                        true
                    ),
                before: false,
                GlobalFilter.FilterType.Delete
            );

            return fsql;
        }

        public static object CallMethod(METHOD method, params object[] args)
        {
            try
            {
                // 1. 验证方法和参数
                ValidateMethod(method);

                // 2. 生成缓存键
                var cacheKey = GenerateCacheKey(method);

                // 3. 检查缓存
                if (PluginManager.TryGetValue(cacheKey, out var pluginManager))
                {
                    return ExecuteCachedMethod(pluginManager, method, args);
                }

                // 4. 生成源代码（使用T4模板）
                var sourceCode = GenerateSourceCode(method);

                // 5. 编译程序集
                var assembly = CompileToAssembly(sourceCode, method.NAME);

                // 6. 创建插件管理器
                pluginManager = CreatePluginManager(assembly, method.NAME);

                // 7. 缓存插件管理器
                PluginManager[cacheKey] = pluginManager;

                // 8. 执行方法
                return ExecuteMethodFromAssembly(assembly, method.NAME, args);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error executing method {method.NAME}: {ex.Message}");
                throw;
            }
        }

        private static void ValidateMethod(METHOD method)
        {
            if (method == null)
            {
                throw new ArgumentNullException(nameof(method));
            }

            // 仅处理 C# 类型的方法
            if (method.METHOD_TYPE?.ToLower() != "c#")
            {
                throw new NotSupportedException(
                    $"Method type '{method.METHOD_TYPE}' is not supported. Only C# methods are supported."
                );
            }

            // 检查方法代码是否为空
            if (string.IsNullOrWhiteSpace(method.METHOD_CODE))
            {
                throw new ArgumentException("Method code cannot be empty.");
            }

            // 检查方法名称是否为空
            if (string.IsNullOrWhiteSpace(method.NAME))
            {
                throw new ArgumentException("Method name cannot be empty.");
            }
        }

        private static string GenerateCacheKey(METHOD method)
        {
            // 基于方法名称和代码哈希值生成缓存键
            using var sha256 = System.Security.Cryptography.SHA256.Create();
            var codeBytes = System.Text.Encoding.UTF8.GetBytes(method.METHOD_CODE ?? "");
            var hashBytes = sha256.ComputeHash(codeBytes);
            var hashString = BitConverter.ToString(hashBytes).Replace("-", "").Substring(0, 8);
            return $"{method.NAME}_{hashString}";
        }

        private static string GenerateSourceCode(METHOD method)
        {
            // 使用T4模板生成源代码
            return T4TemplateProcessor.GenerateSourceCode(method);
        }

        private static System.Reflection.Assembly CompileToAssembly(
            string sourceCode,
            string methodName
        )
        {
            try
            {
                var pluginDir = Path.Combine(AppContext.BaseDirectory, "Plugins");
                if (!Directory.Exists(pluginDir))
                {
                    Directory.CreateDirectory(pluginDir);
                }

                var pluginPath = Path.Combine(pluginDir, $"{methodName}.dll");

                // 保存源代码到文件，便于调试
                var sourceFilePath = Path.Combine(pluginDir, $"{methodName}.cs");
                File.WriteAllText(sourceFilePath, sourceCode);
                Console.WriteLine($"Source code saved to: {sourceFilePath}");

                if (CsCompiler.CompileMethod2Dll(sourceCode, pluginPath))
                {
                    var pluginLoader = new PluginLoader();
                    return pluginLoader.LoadPlugin(pluginPath);
                }
                else
                {
                    // 尝试获取更详细的编译错误信息
                    var errorLogPath = Path.Combine(pluginDir, $"{methodName}.errors.txt");
                    if (File.Exists(errorLogPath))
                    {
                        var errorDetails = File.ReadAllText(errorLogPath);
                        throw new InvalidOperationException($"Failed to compile method {methodName}. Errors:\n{errorDetails}");
                    }
                    
                    throw new InvalidOperationException($"Failed to compile method {methodName}. No detailed error information available.");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error compiling method {methodName}: {ex.Message}");
                // 输出源代码用于调试
                Console.WriteLine($"Source code that failed to compile:\n{sourceCode}");
                throw;
            }
        }

        private static HotReloadPluginManager CreatePluginManager(
            System.Reflection.Assembly assembly,
            string methodName
        )
        {
            try
            {
                var pluginDir = Path.Combine(AppContext.BaseDirectory, "Plugins");
                var pluginPath = Path.Combine(pluginDir, $"{methodName}.dll");

                var pluginManager = new HotReloadPluginManager(pluginPath);

                pluginManager.PluginReloaded += (loadedAssembly) =>
                {
                    Console.WriteLine($"Plugin {methodName} reloaded successfully.");
                };

                pluginManager.ReloadFailed += (ex) =>
                {
                    Console.WriteLine($"Failed to reload plugin {methodName}: {ex.Message}");
                };

                pluginManager.LoadInitialPlugin();

                return pluginManager;
            }
            catch (Exception ex)
            {
                Console.WriteLine(
                    $"Error creating plugin manager for method {methodName}: {ex.Message}"
                );
                throw;
            }
        }

        private static object ExecuteCachedMethod(
            HotReloadPluginManager pluginManager,
            METHOD method,
            object[] args
        )
        {
            try
            {
                var className = method.NAME.Replace(" ", "_");
                return pluginManager.ExecuteMethod($"KrasDD.DyMethods.{className}", "Run", args);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error executing cached method {method.NAME}: {ex.Message}");
                throw;
            }
        }

        private static object ExecuteMethodFromAssembly(
            System.Reflection.Assembly assembly,
            string methodName,
            object[] args
        )
        {
            try
            {
                var className = methodName.Replace(" ", "_");
                var typeName = $"KrasDD.DyMethods.{className}";

                var type = assembly.GetType(typeName);
                if (type == null)
                {
                    throw new TypeLoadException($"Type {typeName} not found in assembly");
                }

                var method = type.GetMethod("Run");
                if (method == null)
                {
                    throw new MissingMethodException($"Method Run not found in type {typeName}");
                }

                var instance = Activator.CreateInstance(type);
                return method.Invoke(instance, args);
            }
            catch (Exception ex)
            {
                Console.WriteLine(
                    $"Error executing method from assembly {methodName}: {ex.Message}"
                );
                throw;
            }
        }
    }
}
